package agent

import (
	corev1 "gluenet/internal/agent/api/core/v1"
	"gluenet/internal/agent/cache/object"
	"gluenet/internal/agent/depend"
	"gluenet/internal/agent/driver/hostdriver"
	"gluenet/internal/agent/driver/rpcdriver"
	"gluenet/internal/agent/monitor"
	"gluenet/internal/agent/monitor/option"

	"github.com/spf13/pflag"
	"github.com/spf13/viper"
	"golang.org/x/sync/errgroup"
)

const (
	keyWithComponentes = "with.components"
)

func init() {
	registerFlags(initDaemonFlags)
}

func initDaemonFlags(flags *pflag.FlagSet) {
	flags.Bool(
		keyWithComponentes,
		false,
		"Flag to let agent init monitoring components",
	)
}

type daemon struct {
	closeFuncs []func() error
}

func NewDaemon() *daemon {
	return &daemon{}
}

// 进行agent注册、监控组件、agent的初始化
func (s *daemon) Initialize(vp *viper.Viper) {
	var (
		err      error
		cfg      *corev1.Agent
		agentObj *corev1.AgentObj
		hostObj  *corev1.HostObj
		objects  object.Interface
	)

	if cfg, err = NewConfigBuilder().
		Build(vp); err != nil {
		goto ErrProcess
	}
	agentObj = &corev1.AgentObj{
		GluenetMeta: corev1.GluenetMeta{
			GluenetGuid: cfg.Guid,
			GluenetKind: `agent`,
		},
		Spec:   *cfg,
		Status: corev1.Status{State: corev1.StatusNotReady},
	}

	hostObj = &corev1.HostObj{
		GluenetMeta: corev1.GluenetMeta{
			GluenetGuid: cfg.Guid,
			GluenetKind: `host`,
		},
		Spec: hostdriver.GetHost(),
	}

	objects = depend.Produce[object.Interface]()
	if err = objects.Put(
		agentObj.GluenetGuid,
		agentObj.GluenetKind,
		agentObj,
	); err != nil {
		goto ErrProcess
	}

	if err = objects.Put(
		hostObj.GluenetGuid,
		hostObj.GluenetKind,
		hostObj,
	); err != nil {
		goto ErrProcess
	}

	if !vp.GetBool(keyWithComponentes) {
		goto InitAgent
	}

	if mnt := depend.Produce[monitor.Interface](); mnt != nil {

		if err = mnt.Init(defaultMonitorOptions()); err != nil {
			goto ErrProcess
		}
		s.closeFuncs = append(s.closeFuncs, mnt.Close)

	}

InitAgent:
	if agent := depend.Produce[*AgentServer](); agent != nil {

		agent.Init()
		s.closeFuncs = append(s.closeFuncs, agent.Close)
		agentObj.Status.State = corev1.StatusReady

	}

	return

ErrProcess:
	logger.Fatalf(err.Error())
}

func defaultMonitorOptions() *option.MonitorOptions {
	var opts *option.MonitorOptions
	depend.Invoke(func(cfg *corev1.Agent, rpc *rpcdriver.RpcConfig) {
		opts = &option.MonitorOptions{
			Guid:       cfg.Guid,
			Nats:       rpc.NatsUrl,
			Components: cfg.MonitorComponents,
		}
	})

	return opts
}

func (s *daemon) Close() error {
	var eg errgroup.Group
	for _, closeFunc := range s.closeFuncs {
		eg.Go(closeFunc)
	}
	return eg.Wait()
}
